<template>
  <div class="design-page">
    <page-header title="返回规则列表"
                 @back="goBack">
      <template slot="content">
        <div>
          <div style="font-weight: bold">【{{ gzflmc }}】{{ gzdyItem.gzmc }}</div>
          <div style="font-size: 14px">{{ gzdyItem.gzms }}</div>
        </div>
        <div style="flex: 1;text-align: right;">
          <el-button type="primary" @click="save">保存</el-button>
        </div>
      </template>
    </page-header>
    <div class="content-box">
      <el-card v-show="topVisible">
        <div style="text-align: right">
          <el-button type="primary" @click="importBds()">导入</el-button>
          <el-button type="primary" @click="editBds()">新增</el-button>
        </div>
        <el-table
            :data="xsBdsList"
            stripe
            style="width: 100%">
          <el-table-column
              align="center"
              label="表达式名称"
              prop="bdsmc"
              width="100px">
          </el-table-column>
          <el-table-column
              label="表达式描述"
              prop="bdsms"
              show-overflow-tooltip>
          </el-table-column>
          <el-table-column
              label="表达式"
              prop="bds"
              show-overflow-tooltip>
          </el-table-column>
          <el-table-column
              align="center"
              label="表达式类型"
              prop="bdslx"
              width="100px">
            <template slot-scope="{row}">
              {{ bdslxObj[row.bdslx] }}
            </template>
          </el-table-column>
          <el-table-column
              label="创建时间"
              prop="cjsj"
              width="180">
          </el-table-column>
          <el-table-column
              label="操作"
              width="140px">
            <template slot-scope="{row}">
              <el-link type="primary" @click="duplicate(row)">复制</el-link>
              <el-link style="margin-left: 15px" type="primary" @click="editBds(row)">编辑</el-link>
              <el-link style="margin-left: 15px" type="danger" @click="deleteBds(row)">删除</el-link>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <el-card>
        <div style="text-align: right">
          <div :class="{active:!topVisible}" :title="topVisible?'全屏':'取消全屏'" class="fullScreen"
               @click="fullScreen"></div>
          <el-button style="margin-right: 5px" type="primary" @click="exportTestCase">导出测试用例</el-button>
          <el-button style="margin-right: 15px" type="primary" @click="testRule">测试</el-button>
          <el-popconfirm
              confirm-button-type="danger"
              title="是否重置？"
              @confirm="reset"
          >
            <el-button slot="reference" plain type="danger">重置</el-button>
          </el-popconfirm>
        </div>
        <gz-editor v-if="bdsLoaded" ref="gzDesign" :bds-list="xsBdsList" :bds-name-obj="bdsNameObj"
                   :gzbds="gzdy.gzbds"/>
      </el-card>
    </div>
    <xj-dialog :visible.sync="addBdsDialogVisible"
               height="calc(100% - 30vh)"
               title="表达式设计"
               width="1200px">
      <js-bds-new ref="bds" :all-zd-list="allZdList" :bds-obj="bdsForm"/>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleAddBdsBtnClick(false)">取消</el-button>
        <el-button type="primary" @click="handleAddBdsBtnClick(true)">确定</el-button>
      </div>
    </xj-dialog>
    <xj-dialog :visible="ruleTestDialogVisible" title="规则测试" width="80%">
      <el-form :model="ruleTestForm" label-width="110px">
        <el-form-item label="数据获取来源">
          <el-radio-group v-model="ruleTestForm.sjly">
            <el-radio-button :label="0">输入数据</el-radio-button>
            <el-radio-button :label="1">远程请求</el-radio-button>
          </el-radio-group>
        </el-form-item>
        <el-form-item v-show="ruleTestForm.sjly" label="数据获取地址">
          <el-input v-model="ruleTestForm.url">
            <template slot="prepend">
              <el-select v-model="ruleTestForm.xy" placeholder="请选择" style="width: 85px;">
                <el-option label="http://" value="http"></el-option>
                <el-option label="https://" value="https"></el-option>
              </el-select>
            </template>
            <template slot="append">
              <el-link>获取数据</el-link>
            </template>
          </el-input>
        </el-form-item>
        <el-form-item v-show="ruleTestForm.sjly" label="数据获取参数">
          <vue-json-editor v-model="ruleTestForm.testParam" lang="zh" mode="code"/>
        </el-form-item>
        <el-form-item v-show="!ruleTestForm.sjly" label="输入待校验数据">
          <vue-json-editor v-model="ruleTestForm.data" lang="zh" mode="code"/>
        </el-form-item>
        <el-form-item v-show="ruleTestForm.sjly" label="待校验数据">
          <vue-json-editor v-model="ruleTestForm.data" lang="zh" mode="view"/>
        </el-form-item>
        <el-form-item label="测试结果">
          <vue-json-editor v-model="ruleTestForm.testResult" lang="zh" mode="view"/>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <div class="footer-box">
          <div>
            <div style="display:inline-block;text-indent: 100px;vertical-align: top;">
              核查数量：<span>{{ ruleTestForm.testResult.hcsl || 0 }}</span></div>
            <div style="display:inline-block;margin-left: 20px;">
              风险数量：<span>{{ ruleTestForm.testResult.fxList.length }}</span>
              <span v-if="ruleTestForm.testResult.fxList.length"
                    style="color:red;display:inline-block;margin-left: 20px;">存在风险</span>
            </div>
          </div>
          <div class="btn-box">
            <el-button @click="handleRuleTestBtnClick(false)">关闭</el-button>
            <el-button type="primary" @click="handleRuleTestBtnClick(true)">测试</el-button>
          </div>
        </div>
      </div>
    </xj-dialog>
    <xj-dialog :visible="importBdsVisible" title="请选择导入的表达式" width="80%">
      <el-cascader-panel v-model="selectBds" :props="props"></el-cascader-panel>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleImportBdsBtnClick(false)">取消</el-button>
        <el-button type="primary" @click="handleImportBdsBtnClick(true)">确定</el-button>
      </div>
    </xj-dialog>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import vueJsonEditor from 'vue-json-editor'
import commonMixins from "@/mixins/commonMixins";
import XjDialog from "@/components/XjDialog";
import GzEditor from "@/components/GzEditorNew";
import PageHeader from "@/components/PageHeader";

import JsBdsNew from "@/components/JsBdsNew";

import util from '../util/util'

import axios from "axios";

// 字段名称支持数字和中文
let reg = /[A-Za-z0-9_]+\.[A-Za-z0-9_\u4e00-\u9fa5]+/g

function getZdmcList (bds) {
  if (!bds) return []
  let arr = bds.match(reg)
  if (!arr) return []
  return arr.map(item => item)
}

export default {
  name: "Design",
  mixins: [commonMixins],
  components: {
    XjDialog, GzEditor, PageHeader,
    vueJsonEditor, JsBdsNew
  },
  data () {
    return {
      props: {
        lazy: true,
        multiple: true,
        lazyLoad: async (node, resolve) => {
          const { level, value } = node;
          if (level === 0) {
            // 加载规则分类
            let gzflList = await this.$api.queryGzflListByXmbm({ xmbm: this.xmItem.xmbm })
            resolve(gzflList.map(item => {
              return {
                label: item.gzflmc,
                value: item.gzflbm,
                leaf: false
              }
            }))
          } else if (level === 1) {
            // 加载规则定义
            let gzdyList = await this.$api.queryGzdyListByGzflbm({ gzflbm: value })
            resolve(gzdyList.filter(item => item.gzbm !== this.gzdy.gzbm).map(item => {
              return {
                label: item.gzmc,
                value: item.gzbm,
                leaf: false
              }
            }))
          } else if (level === 2) {
            // 加载表达式
            let bdsList = await this.$api.queryBdsListByGzbm({ gzbm: value })
            resolve(bdsList.map(item => {
              return {
                label: item.bdsms,
                value: item.bdsbm,
                leaf: true
              }
            }))
          } else {
            resolve([])
          }
        },
      },
      gzdy: { xgjy: {} },
      topVisible: true,
      bdsList: [],
      bdsLoaded: false,
      bdslxObj: {
        JS: "计算",
        TS: "提示",
        XG: "修改"
      },
      bdsForm: {
        bdsbm: '',
        bdsmc: '',
        bdsms: '',
        bdslx: 'JS',
        hczdList: [],
        bds: '',
        jslx: '',
        p0: '',
        p1: '',
        p2: ''
      },
      addBdsDialogVisible: false,
      ruleTestDialogVisible: false,
      ruleTestForm: {
        sjly: 0,
        data: [],
        url: '',
        xy: 'http',
        testParam: {},
        testResult: {
          fxList: []
        }
      },
      importBdsVisible: false,
      selectBds: [],
      gzflmc: ''
    }
  },
  computed: {
    ...mapState(['stTree', 'stTreeObj', "mxstgxList"]),
    xsBdsList () {
      return this.bdsList.filter(item => !item.remove)
    },
    bdsNameObj () {
      return this.xsBdsList.reduce((_res, item) => {
        if (!_res[item.bdsmc]) _res[item.bdsmc] = []
        let res = getZdmcList(item.bds)
        if (res) res.forEach(zdmc => {
          let obj = this.stTreeObj[zdmc];
          if (!obj) {
            let sarr = zdmc.split('.')
            let zdzwmc = zdmc
            let st = this.stTree.find(stdy => stdy.stbm === sarr[0])
            if (st) zdzwmc = `${st.stmc}.${sarr[1]}`
            obj = { zdzwmc: zdzwmc, kjlx: '', sjybm: '', glcs: '{}', sjydz: '' }
          }
          let o = {
            bdsmc: item.bdsmc, zdmc: zdmc, ...obj,
            bdslx: item.bdslx,
            option: JSON.stringify({ kjlx: obj.kjlx, sjybm: obj.sjybm, glcs: obj.glcs, sjydz: obj.sjydz })
          }
          _res[o.bdsmc].push(o)
        })
        return _res
      }, {})
    },
    allZdList () {
      let arr = []
      for (let bdsmc in this.bdsNameObj) {
        this.bdsNameObj[bdsmc].forEach(item => {
          let aa = arr.find(_item => _item.zdmc === item.zdmc)
          if (aa) {
            if (aa.bdslx !== item.bdslx) {
              aa.bdslx = 'JS'
            }
            return
          }
          arr.push(item)
        })
      }
      return arr;
    }
  },
  mounted () {
    window.addEventListener('beforeunload', this.beforeunload)
    this.init()
    this.$store.dispatch("initDesignData")
  },
  methods: {
    goBack () {
      this.$router.push(`/xmsy`)
    },
    beforeunload (e) {
      e.returnValue = '';
    },
    importBds () {
      this.selectBds = []
      this.importBdsVisible = true
    },
    async handleImportBdsBtnClick (isConfirm) {
      if (isConfirm) {
        let arr = await Promise.all(this.selectBds.map(item => this.$api.queryBdsByBdsbm({ bdsbm: item[2] })))
        for (let arrElement of arr) {
          arrElement.bdsbm = ''
          arrElement.bdsmc = ''
          arrElement.cjsj = ''
          this.addBdsToList(arrElement)
        }
      }
      this.importBdsVisible = false
    },
    async loadGzdy () {
      let res = null
      try {
        res = await this.$api.queryGzdyByGzbm({ gzbm: this.gzdyItem.gzbm })
      } catch (e) {
        this.$message({ message: e.message, type: 'error' })
        return this.goBack()
      }
      this.gzdy = res
    },
    async init () {
      await this.loadGzdy()

      if (!this.gzdy.gzbm) {
        this.bdsList = []
        this.bdsLoaded = true
        return
      }
      this.$api.queryGzflByGzflbm({ gzflbm: this.gzdy.gzflbm }).then(res => this.gzflmc = res.gzflmc)
      // 查询规则定义 查询表达式列表
      let bdsList = await this.$api.queryBdsListByGzbm({ gzbm: this.gzdy.gzbm })

      bdsList.sort((a, b) => {
        let aa = parseInt(a.bdsmc.replace('bds', ''))
        let bb = parseInt(b.bdsmc.replace('bds', ''))
        return aa - bb;
      })
      this.bdsList = bdsList;
      this.bdsLoaded = true
    },
    deleteBds (bds) {
      if (bds.bdsbm) {
        this.$set(bds, "remove", true)
      } else {
        let idx = this.bdsList.findIndex(item => !item.bdsbm && item.bdsmc === bds.bdsmc)
        if (idx > -1) {
          this.bdsList.splice(idx, 1)
        }
      }
    },
    duplicate (bds) {
      bds = JSON.parse(JSON.stringify(bds))
      bds.bdsbm = ''
      bds.bdsmc = ''
      bds.cjsj = ''
      this.addBdsToList(bds)
    },
    addBdsToList (bdsObj) {
      let hczdList = bdsObj.hczdList.map(_item => {
        return {
          zdmc: _item.zdmc,
          option: _item.option
        }
      })

      bdsObj.hczdList = hczdList
      bdsObj.hczd = JSON.stringify(hczdList)

      if (!bdsObj.bdsms) return this.$message({ message: '表达式描述不能为空', type: "error" })
      if (/p\d/.test(bdsObj.bds)) return this.$message({ message: '表达式不完整', type: "error" })
      let num = this.xsBdsList.reduce((res, item) => {
        let _res = parseInt(item.bdsmc.replace("bds", ''))
        return _res >= res ? _res : res
      }, 0)
      if (!bdsObj.bdsmc) {
        bdsObj.bdsmc = `bds${num + 1}`
      }
      let idx = -1
      if (bdsObj.bdsbm) {
        idx = this.bdsList.findIndex(item => item.bdsbm === bdsObj.bdsbm)
      } else {
        idx = this.bdsList.findIndex(item => !item.bdsbm && item.bdsmc === bdsObj.bdsmc)
      }
      if (idx > -1) {
        this.$set(this.bdsList, idx, bdsObj)
      } else {
        this.bdsList.push(bdsObj)
      }
    },
    editBds (bds) {
      if (!bds) {
        bds = {
          bdsbm: '',
          gzbm: '',
          bdsmc: '',
          bdsms: '',
          bds: '',
          cjsj: '',
          bdslx: 'JS',
          hczdList: [],
          hczd: '[]'
        }
      }
      this.bdsForm = bds
      this.addBdsDialogVisible = true;
    },
    async handleAddBdsBtnClick (isConfirm) {
      if (isConfirm) {
        let bdsEl = this.$refs.bds
        if (!bdsEl) return
        let bdsObj;
        try {
          bdsObj = bdsEl.getBdsForm()
        } catch (e) {
          return this.$message({ message: e.message, type: 'error' })
        }

        if (bdsObj.bdslx === "TS") {
          if (this.xmItem?.xmpz?.sfbthczd) {
            if (!bdsObj.hczdList.length) {
              return this.$message.error("核查字段-不能为空")
            }
          }

          let allHczdList = this.allZdList.filter(item => item.bdslx === 'JS')

          for (const item of bdsObj.hczdList) {
            if (allHczdList.every(it => it.zdmc !== item.zdmc)) {
              return this.$message.error(`核查字段-存在未使用的字段：${item.zdzwmc || item.zdmc}`)
            }
          }

        }

        let idx = util.check(bdsObj.bds)
        if (idx) {
          try {
            let bds = bdsObj.bds
            let a = ""
            let charCode = -1
            for (let i = 0; i < idx - 1; i++) {
              charCode = bds.charCodeAt(i);
              if (charCode >= 0 && charCode <= 128) a += "-"
              else a += "--"
            }
            let len = 40
            let oldIdx = idx
            while (idx > len) idx = idx - len
            bds = bds.substr(oldIdx - idx, len)
            a = a.substr(oldIdx - idx, len)
            let b = ''
            for (let i = 0; i < bds.length; i++) {
              if (i !== bds.length - 1) b += bds.charAt(i)
              else b += `<span style="color:red">${bds.charAt(i)}</span>`
            }

            await this.$confirm(`column：${oldIdx}<br>${b}<br>${a}<span style="color:red">^</span><br>表达式括号不平衡，是否继续？`, "提示", {
              showClose: false,
              dangerouslyUseHTMLString: true
            })
          } catch (e) {
            return
          }
        }

        this.addBdsToList(bdsObj)
      }
      this.bdsForm = {}
      this.addBdsDialogVisible = false;
    },
    getRuleInfo (lx = "save") {
      let gzDesign = this.$refs.gzDesign
      let gzbds = gzDesign.getGzbds();

      let zdjh = this.allZdList.map(item => item.zdmc)
      let bmjh = zdjh.map(zdmc => zdmc.split(".")[0])
      bmjh = Array.from(new Set(bmjh))
      let bdsList = []
      if (lx === 'save') {
        bdsList = this.bdsList
      } else if (lx === 'test') {
        bdsList = this.xsBdsList
      }

      let ruleInfo = {
        gzdy: {
          gzbm: this.gzdy.gzbm,
          gzmc: this.gzdy.gzmc,
          gzms: this.gzdy.gzms,
          gzbds: gzbds,
          bmjh: JSON.stringify(bmjh),
          zdjh: JSON.stringify(zdjh),
          gzlx: this.gzdy.gzlx,
          wtlx: this.gzdy.wtlx,
          xgjy: this.gzdy.xgjy,
          gzpz: {}
        },
        bdsList: bdsList
      }
      if (!ruleInfo.gzdy.gzbm) {
        ruleInfo.gzdy.gzflbm = this.gzdy.gzflbm
      }
      ruleInfo.gzdy.gzpz = JSON.stringify(ruleInfo.gzdy.gzpz)
      return ruleInfo
    },
    async save () {
      let ruleInfo;
      try {
        ruleInfo = this.getRuleInfo();
      } catch (e) {
        return this.$message({ message: e.message, type: 'error' })
      }

      let gzdy = ruleInfo.gzdy
      try {
        // 保存规则表达式
        await this.$api.updateGzbds({
          gzbm: gzdy.gzbm,
          gzbds: gzdy.gzbds,
          bmjh: gzdy.bmjh,
          zdjh: gzdy.zdjh,
        })
      } catch (e) {
        return this.$message({ message: e.message, type: "error" })
      }
      // 保存表达式
      let bdsList = ruleInfo.bdsList
      let fail = []
      for (let bds of bdsList) {
        try {
          if (bds.remove) {
            await this.$api.deleteBds({ bdsbm: bds.bdsbm })
          } else if (bds.bdsbm) {
            await this.$api.updateBds(bds)
          } else {
            bds.gzbm = gzdy.gzbm
            bds.bdsbm = await this.$api.addBds(bds)
          }
        } catch (e) {
          fail.push(bds.bdsmc + ":" + e.message + ";")
        }
      }
      if (!fail.length) {
        this.init()
        this.$message({ message: '保存成功', type: "success" })
      } else {
        this.$message({ message: '表达式没有全部保存成功，' + fail.join(","), type: "warning" })
      }
      this.init()
    },
    reset () {
      let gzDesign = this.$refs.gzDesign
      gzDesign.reset()
    },
    fullScreen () {
      this.topVisible = !this.topVisible
      let gzDesign = this.$refs.gzDesign
      gzDesign.fullScreen(!this.topVisible)
    },
    exportTestCase () {
      let ruleInfo;
      try {
        ruleInfo = JSON.parse(JSON.stringify(this.getRuleInfo()));
      } catch (e) {
        return this.$message({ message: e.message, type: 'error' })
      }

      let items = []
      let gzdy = ruleInfo.gzdy

      let getBdszwmcList = bds => {
        let a = getBdsmcList(bds)
        return a.map(item => this.xsBdsList.find(it => it.bdsmc === item).bdsms)
      }


      function getList (gzbds, p, i = 0) {
        let list = []

        for (const yesElement of gzbds.yes) {
          let items = getList(yesElement, gzbds, i + 1)
          console.log(i, 'yes', items.length)
          // let a = getBdszwmcList(yesElement.bds)
          // a.forEach(item => {
          //   items.forEach(it => {
          //     it.unshift(`满足【${yesElement.bds}】`)
          //   })
          // })
          // list.unshift(...items)
          list.push(...items)
        }
        for (const noElement of gzbds.no) {
          let items = getList(noElement, gzbds, i + 1)
          console.log(i, 'no', items.length)
          // let a = getBdszwmcList(noElement.bds)
          // a.forEach(item => {
          //   items.forEach(it => {
          //     it.unshift(`不满足【${noElement.bds}】`)
          //   })
          // })
          // list.unshift(...items)
          list.push(...items)
        }
        list.unshift(i + gzbds.bds)
        console.log(i, list.length, gzbds.bds)
        return list
      }

      let gzbds = gzdy.gzbds
      console.log(getList(gzbds))
    },
    testRule () {
      try {
        this.getRuleInfo();
      } catch (e) {
        return this.$message({ message: e.message, type: 'error' })
      }

      let testFormObj = myLocalStorage.getItem("testFormObj", {})
      let testForm = testFormObj[this.gzdy.gzflbm] || { sjly: 0, url: '', testParam: {}, data: [], xy: 'http' }
      this.ruleTestForm = {
        sjly: testForm.sjly,
        url: testForm.url,
        testParam: testForm.testParam,
        data: testForm.data,
        xy: testForm.xy,
        testResult: { fxList: [] }
      }
      this.ruleTestDialogVisible = true
    },
    async handleRuleTestBtnClick (isConfirm) {
      if (isConfirm) {
        let ruleInfo;
        try {
          ruleInfo = this.getRuleInfo('test');
        } catch (e) {
          return this.$message({ message: e.message, type: 'error' })
        }

        if (this.ruleTestForm.sjly === 1) {
          try {
            let res = await axios.post(this.ruleTestForm.xy + "://" + this.ruleTestForm.url, {
              ...this.ruleTestForm.testParam,
              ruleInfos: [ruleInfo],
              mxstgxList: this.mxstgxList
            })
            this.ruleTestForm.data = res
          } catch (e) {
            return this.$message({ message: e.message, type: 'error' })
          }
        }

        let testFormObj = myLocalStorage.getItem("testFormObj", {})
        testFormObj[this.gzdy.gzflbm] = {
          sjly: this.ruleTestForm.sjly,
          url: this.ruleTestForm.url,
          xy: this.ruleTestForm.xy,
          data: this.ruleTestForm.data,
          testParam: this.ruleTestForm.testParam
        }
        myLocalStorage.setItem("testFormObj", testFormObj)

        try {
          let param = { ruleInfos: [ruleInfo], data: this.ruleTestForm.data }
          let testResult = await this.$api.testRule(param)

          let cwxx = ''
          for (let gzjyLogListElement of testResult.gzjyLogList) {
            if (!gzjyLogListElement.sfcg) {
              cwxx += cwxx ? `<br>${gzjyLogListElement.cwxx}` : gzjyLogListElement.cwxx
            }
          }
          if (cwxx) {
            this.$message.error({
              dangerouslyUseHTMLString: true,
              message: cwxx
            })
          }

          this.ruleTestForm.testResult = testResult
        } catch (e) {
          return this.$message({ message: e.message, type: 'error' })
        }
      } else {
        this.ruleTestDialogVisible = false
        this.ruleTestForm = { sjly: 0, url: '', testParam: {}, testResult: { fxList: [] }, data: [] }
      }
    }
  },
  beforeDestroy () {
    window.removeEventListener('beforeunload', this.beforeunload)
  }
}
</script>

<style lang="scss" scoped>
.design-page {
  display: flex;
  flex-direction: column;

  .content-box {
    flex: 1;
    overflow: auto;

    .fullScreen {
      display: inline-block;
      margin-right: 15px;
      background-position: center;
      width: 28px;
      height: 28px;
      background-size: 80%;
      vertical-align: top;
      background-repeat: no-repeat;
      background-image: url("~@/assets/svg/jrqp.svg");
      cursor: pointer;

      &:hover {
        background-color: #ccc;
      }

      &.active {
        background-image: url("~@/assets/svg/tcqp.svg");
      }
    }
  }

  .footer-box {
    display: flex;

    .btn-box {
      flex: 1;
    }
  }
}
</style>
